home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume4 / fmtr < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  30.3 KB

  1. Subject: Fmtr - simple text formatter
  2. Newsgroups: mod.sources
  3. Approved: jpn@panda.UUCP
  4.  
  5. Mod.sources:  Volume 4, Issue 120
  6. Submitted by: harvard!hscfvax!popvax!mohamed (Mohamed ellozy)
  7.  
  8.  
  9. Fmtr is a simple text formatter which evens out the lines of
  10. files prepared for submission to the Unix formatters nroff,
  11. troff, or ditroff.
  12.  
  13.      It is for users who find editing neatly formatted files
  14. pleasanter than working with files with irregular lines.  It
  15. knows that lines starting with a period or with an apostrophe
  16. should be output as is, and that certain requests and macros
  17. start no-fill mode, while others return to fill mode.  So, unlike
  18. fmt, it will process the following example properly:
  19.  
  20.      .TS
  21.      n n.
  22.      1       2
  23.      3       4
  24.      .TE
  25.  
  26. Fmt run on that example would produce:
  27.  
  28.      .TS
  29.      n n.  1       2 3       4
  30.      .TE
  31.  
  32. destroying the table.
  33.  
  34. Mohamed el Lozy                mohamed@hscfvax.uucp
  35. Health Sciences Computing Facility    ...!harvard!hscfvax!mohamed
  36. Harvard School of Public Health
  37. 665 Huntington Avenue
  38. Boston, MA 02115
  39.  
  40. ------------------------------  CUT HERE  ------------------------------
  41. #! /bin/sh
  42. # This is a shell archive, meaning:
  43. # 1. Remove everything above the #! /bin/sh line.
  44. # 2. Save the resulting text in a file.
  45. # 3. Execute the file with /bin/sh (not csh) to create the files:
  46. #    README
  47. #    Makefile
  48. #    command.c
  49. #    efopen.3s
  50. #    efopen.c
  51. #    fgetsmod.3s
  52. #    fgetsmod.c
  53. #    fmtr.1
  54. #    fmtr.c
  55. #    fmtr.h
  56. #    getopt.3
  57. #    getopt.c
  58. #    lowtext.c
  59. # This archive created: Thu May 15 16:20:30 1986
  60. export PATH; PATH=/bin:$PATH
  61. echo shar: extracting "'README'" '(1089 characters)'
  62. if test -f 'README'
  63. then
  64.     echo shar: will not over-write existing file "'README'"
  65. else
  66. cat << \SHAR_EOF > 'README'
  67. The shell archive contains the files needed to make fmtr, a formatter
  68. for {n,t,dit}roff input files.  Four of the source files:
  69.  
  70.     fmtr.h
  71.     fmtr.c
  72.     command.c
  73.     lowtext.c
  74.  
  75. contain the program proper.  In addition it uses three functions that
  76. I have put in my library:
  77.  
  78.     getopt()    a public domain version of the System V utility
  79.             apparently due to Henry Spencer taht I have
  80.             modified minimally
  81.     efopen()    a modified version of K & P's function
  82.     fgetsmod()    a modified version of fgets() that truncates
  83.             lines longer than the array they are being read
  84.             into.
  85.  
  86. Manual pages for fmtr and for the three functions named above are
  87. included.  The Makefile does not have any provision for adding these
  88. functions to any library.  I have added them to /lib/libc.a, you may
  89. want to put them elsewhere.  I have also not put in any provision for
  90. installing the manual pages associated with them.
  91.  
  92. Comments, bug reports etc to:
  93.  
  94. Mohamed el Lozy                mohamed@hscfvax.uucp
  95. Health Sciences Computing Facility    ...!harvard!hscfvax!mohamed
  96. Harvard School of Public Health
  97. 665 Huntington Avenue
  98. Boston, MA 02115
  99. SHAR_EOF
  100. if test 1089 -ne "`wc -c < 'README'`"
  101. then
  102.     echo shar: error transmitting "'README'" '(should have been 1089 characters)'
  103. fi
  104. chmod +x 'README'
  105. fi
  106. echo shar: extracting "'Makefile'" '(789 characters)'
  107. if test -f 'Makefile'
  108. then
  109.     echo shar: will not over-write existing file "'Makefile'"
  110. else
  111. cat << \SHAR_EOF > 'Makefile'
  112. CFLAGS = -O
  113.  
  114. # comment out next line if you have getopt() in your library
  115. GETOPT = getopt.o
  116.  
  117. # comment out next line if you have efopen() in your library
  118. EFOPEN = efopen.o
  119.  
  120. # comment out next line if you have fgetsmod() in your library
  121. FGETSMOD = fgetsmod.o
  122.  
  123. SRC = fmtr.c command.c lowtext.c efopen.c getopt.c fgetsmod.c
  124. OBJ = fmtr.o command.o lowtext.o $(EFOPEN) $(GETOPT) $(FGETSMOD)
  125.  
  126. # You may wish to modify either or both of the following:
  127. BINDIR = /usr/local
  128. MANDIR = /usr/man/man1
  129.  
  130. fmtr:    $(OBJ)
  131.     cc $(CFLAGS) -s -o fmtr $(OBJ)
  132.  
  133. fmtr.o command.o lowtext.o:    fmtr.h
  134.  
  135. install:    fmtr fmtr.1h
  136.     cp -c fmtr $(BINDIR)
  137.     cp -c fmtr.1h $(MANDIR)
  138.  
  139. shar:
  140.     shar -a README Makefile $(SRC) fmtr.h fmtr.1 efopen.3s \
  141.     fgetsmod.3s getopt.3 > fmtr.shar
  142.  
  143. clean:
  144.     /bin/rm -f *.o a.out core fmtr fmtr.shar
  145. SHAR_EOF
  146. if test 789 -ne "`wc -c < 'Makefile'`"
  147. then
  148.     echo shar: error transmitting "'Makefile'" '(should have been 789 characters)'
  149. fi
  150. chmod +x 'Makefile'
  151. fi
  152. echo shar: extracting "'command.c'" '(4522 characters)'
  153. if test -f 'command.c'
  154. then
  155.     echo shar: will not over-write existing file "'command.c'"
  156. else
  157. cat << \SHAR_EOF > 'command.c'
  158. #ifndef lint
  159. static char rcsid[] = "$Header: command.c,v 1.4 86/05/05 14:08:32 root Exp $";
  160. #endif
  161.  
  162. /* command.c decodes command.  Uses binary search in array
  163.  * of struct cmd.
  164.  */
  165.  
  166. #include "fmtr.h"
  167.  
  168. #define HUGE 1000
  169. #define PLUS '+'
  170. #define MINUS '-'
  171.  
  172. #define MAX(x,y) ((x) > (y) ? (x) : (y))
  173. #define MIN(x,y) ((x) < (y) ? (x) : (y))
  174.  
  175. /*  cmdtype is upper case version of command.  All commands of
  176.  *  interest to us are either ce, ul, or else equivalent to
  177.  *  fi or nf
  178.  */
  179.  
  180. enum cmdtype
  181.     {
  182.     CE, UL, FI, NF, OTHER
  183.     } cmd, getcmd();
  184.  
  185. int len;
  186.  
  187. struct cmd {
  188.     char *name;
  189.     enum cmdtype type;
  190. } cmd_table[50] = { "ce", CE,    /* basic nroff requests */
  191.             "ul", UL,
  192.             "nf", NF,
  193.             "fi", FI,
  194.             "TS", NF,    /* universal macros */
  195.             "TE", FI,
  196.             "EQ", NF,
  197.             "EN", FI,
  198.             "PS", NF,
  199.             "PE", FI,
  200.             "IS", NF,
  201.             "IE", FI,
  202.             "DS", NF,    /* ms macros */
  203.             "ID", NF,
  204.             "CD", NF,
  205.             "LD", NF,
  206.             "DE", FI,
  207.             "(b", NF,    /* me macros */
  208.             ")b", FI,
  209.             "(c", NF,
  210.             ")c", FI,
  211.             "(l", NF,
  212.             ")l", FI,
  213.             "(z", NF,
  214.             ")z", FI,
  215.             (char *) NULL, OTHER
  216.         };
  217.  
  218. int val;    /* both are set in getval() */
  219. char argtype;    /* and used in setparam() */
  220.  
  221. /*  command() takes a line starting with some form of the command start
  222.  *  sequence (period or apostrophe, optionally preceded by \&), and
  223.  *  calls getcmd() to return an integer representing the command name,
  224.  *  and then takes appropriate action.
  225.  *
  226.  *  In all cases it produces a break and prints out the command line as is.
  227.  */
  228.  
  229. command(line)
  230. char line[];
  231. {
  232.     cmd = getcmd(line);
  233.  
  234.     if (cmd != OTHER)
  235.     switch (cmd) {
  236.     case CE:
  237.         getval(line);        /* only need getval() with CE and UL */
  238.         ce_val = setparam(ce_val, 1, 0, HUGE);
  239.         break;
  240.     case UL:
  241.         getval(line);
  242.         ul_val = setparam(ul_val, 1, 0, HUGE);
  243.         break;
  244.     case NF:
  245.         nf_val = 1;
  246.         break;
  247.     case FI:
  248.         nf_val = 0;
  249.         break;
  250.     }
  251.  
  252.     n_brk();
  253.     puts(line);
  254. }
  255.  
  256. enum cmdtype getcmd(line)    /* gets command type by binary search */
  257. char *line;        /* stolen from K & R p. 125 with minor changes */
  258. {
  259.     int high, low, mid, cond;
  260.     char *cp;
  261.  
  262.     low = 0;
  263.     high = len - 1;
  264.  
  265.     cp = (*line == '\\') ? line + 3 : line + 1;
  266.  
  267.     while (low <= high) {
  268.     mid = (low + high)/2;
  269.     if ((cond = strncmp(cp, cmd_table[mid].name, 2)) < 0)
  270.         high = mid - 1;
  271.     else if (cond > 0)
  272.         low = mid + 1;
  273.     else
  274.         return(cmd_table[mid].type);
  275.     }
  276.     return(OTHER);
  277. }
  278.  
  279. mk_table(sarray, earray)
  280. char *sarray, *earray;
  281. {
  282.     int mycmp();
  283.     char *cp, *malloc(), *strncpy();
  284.     struct cmd *cmdptr;
  285.  
  286.     if (len == 0) {        /* find end */
  287.     cmdptr = cmd_table;
  288.     while (cmdptr->name)
  289.         cmdptr++;        /* now pointing to NULL ending defined */
  290.     len = cmdptr - cmd_table;
  291.     }
  292.     else
  293.     cmdptr = cmd_table + len;
  294.  
  295.     if (sarray)
  296.     for (cp = sarray; *cp; cp += 3) {
  297.         cmdptr->name = malloc(3);
  298.         (void) strncpy(cmdptr->name, cp + 1, 2);
  299.         cmdptr->type = NF;
  300.         cmdptr++;
  301.     }
  302.  
  303.     if (earray)
  304.     for (cp = earray; *cp; cp += 3) {
  305.         cmdptr->name = malloc(3);
  306.         (void) strncpy(cmdptr->name, cp + 1, 2);
  307.         cmdptr->type = FI;
  308.         cmdptr++;
  309.     }
  310.  
  311.     len = cmdptr - cmd_table;
  312.  
  313.     qsort((char *) cmd_table, len, sizeof cmd_table[0], mycmp);
  314. }
  315.  
  316. mycmp(s1, s2)
  317. struct cmd *s1, *s2;
  318. {
  319.     return(strcmp(s1->name, s2->name));
  320. }
  321.  
  322. /*  getval() gets value of argument preceded by optional sign.  Here
  323.     we are following the nroff rules: commands are exactly two
  324.     letters long followed by optional spaces before arguments.  */
  325.  
  326. getval(line)
  327. char *line;
  328. {
  329.     char *cp;
  330.  
  331.     if (*line == '\\')        /* don't test for z_flag, since otherwise */
  332.     cp = line + 5;        /* we would not be here */
  333.     else
  334.     cp = line + 3;
  335.     for ( ; isspace(*cp); *cp++)
  336.     ;
  337.     argtype = *cp;
  338.     if (argtype == PLUS  ||  argtype == MINUS)
  339.     cp++;
  340.     val = atoi(cp);
  341. }
  342.  
  343. /* setparam() sets parameter.  May be set to param if present and legal,
  344.  * otherwise to def_val, if absent, less than min_val or greater than
  345.  * max_val.
  346.  */
  347.  
  348. setparam(param, def_val, min_val, max_val)
  349. int param, def_val, min_val, max_val;
  350. {
  351.     if (argtype == '\0')
  352.     return(def_val);
  353.     else if (argtype == PLUS)
  354.     param += val;
  355.     else if (argtype == MINUS)
  356.     param -= val;
  357.     else
  358.     param = val;
  359.     param = MAX(param, min_val);
  360.     param = MIN(param, max_val);
  361.  
  362.     return(param);
  363. }
  364.  
  365. #ifdef DEBUG
  366. print_tab()    /* prints table, useful while debugging */
  367. {
  368.     struct cmd *cmdptr;
  369.  
  370.     for (cmdptr = cmd_table; cmdptr < cmd_table + len; cmdptr++)
  371.     printf("%s\n", cmdptr->name);
  372. }
  373. #endif
  374. SHAR_EOF
  375. if test 4522 -ne "`wc -c < 'command.c'`"
  376. then
  377.     echo shar: error transmitting "'command.c'" '(should have been 4522 characters)'
  378. fi
  379. chmod +x 'command.c'
  380. fi
  381. echo shar: extracting "'efopen.3s'" '(918 characters)'
  382. if test -f 'efopen.3s'
  383. then
  384.     echo shar: will not over-write existing file "'efopen.3s'"
  385. else
  386. cat << \SHAR_EOF > 'efopen.3s'
  387. .TH EFOPEN 3S  "11 May 1986"
  388. .SH NAME
  389. efopen \- open a stream, exiting with message in case of failure
  390. .SH SYNOPSIS
  391. .B #include <stdio.h>
  392. .br
  393. .B extern char *progname;
  394. .PP
  395. .SM
  396. .B FILE
  397. .B *efopen(filename, type)
  398. .br
  399. .B char *filename, *type;
  400. .SH DESCRIPTION
  401. .I Efopen
  402. calls
  403. .IR fopen (3S)
  404. to open the file named by
  405. .IR filename ,
  406. and if successful returns a pointer to be used to identify the stream in
  407. subsequent operations.
  408. .PP
  409. If
  410. .I filename
  411. is the string \-
  412. .I efopen
  413. will return
  414. .IR stdin .
  415. .PP
  416. .I Type
  417. is a character string as in
  418. .IR fopen (3S).
  419. .PP
  420. On failure,
  421. .I efopen
  422. exits, after printing out the name of the program (if a value has been
  423. given to
  424. .B progname
  425. by main), the name of the file, and calling
  426. .IR perror (3).
  427. .SH "SEE ALSO"
  428. fopen(3S),
  429. open(2),
  430. fclose(3)
  431. .SH "AUTHOR"
  432. Modified by Mohamed el Lozy from program given in
  433. .I "The UNIX Programming Environment"
  434. by Brian W. Kernighan and Rob Pike, p 182.
  435. SHAR_EOF
  436. if test 918 -ne "`wc -c < 'efopen.3s'`"
  437. then
  438.     echo shar: error transmitting "'efopen.3s'" '(should have been 918 characters)'
  439. fi
  440. chmod +x 'efopen.3s'
  441. fi
  442. echo shar: extracting "'efopen.c'" '(591 characters)'
  443. if test -f 'efopen.c'
  444. then
  445.     echo shar: will not over-write existing file "'efopen.c'"
  446. else
  447. cat << \SHAR_EOF > 'efopen.c'
  448. #ifndef lint
  449. static char rcsid[] = "$Header: efopen.c,v 1.3 86/05/11 09:50:48 root Exp $";
  450. #endif
  451.  
  452. #include <stdio.h>
  453.  
  454. FILE *
  455. efopen(file, mode)    /* fopen file, die if cannot */
  456. char *file, *mode;    /* from K & P with addition of perror() and handling
  457.                of "-" as stdin */
  458. {
  459.     FILE *fp;
  460.     extern char *progname;
  461.  
  462.     if (strcmp(file, "-") == 0)
  463.     return(stdin);
  464.  
  465.     if ((fp = fopen(file, mode)) != NULL)
  466.     return (fp);
  467.  
  468.     if (progname)
  469.     fprintf(stderr, "%s ", progname);
  470.     fprintf(stderr, "can't open file %s mode %s: ", file, mode);
  471.     perror("");
  472.     exit(1);
  473.     /* NOTREACHED */
  474. }
  475. SHAR_EOF
  476. if test 591 -ne "`wc -c < 'efopen.c'`"
  477. then
  478.     echo shar: error transmitting "'efopen.c'" '(should have been 591 characters)'
  479. fi
  480. chmod +x 'efopen.c'
  481. fi
  482. echo shar: extracting "'fgetsmod.3s'" '(1470 characters)'
  483. if test -f 'fgetsmod.3s'
  484. then
  485.     echo shar: will not over-write existing file "'fgetsmod.3s'"
  486. else
  487. cat << \SHAR_EOF > 'fgetsmod.3s'
  488. .TH FGETSMOD 3S  "11 May 1986"
  489. .SH NAME
  490. fgetsmod \- get a line from a stream, truncating it if it is too long
  491. .SH SYNOPSIS
  492. .B #include <stdio.h>
  493. .br
  494. .B #define TOOLONG -2
  495. .PP
  496. .B int *fgets(s, n, stream)
  497. .br
  498. .B char *s;
  499. .br
  500. .SM
  501. .B FILE
  502. .B *stream;
  503. .SH DESCRIPTION
  504. .I Fgetsmod
  505. is a modified version of
  506. .IR fgets ,
  507. which truncates lines longer than the size of the array
  508. into which they are being read.  It reads 
  509. .IR n \-2
  510. characters, or up to a newline
  511. character, whichever comes first,
  512. from the
  513. .I stream
  514. into the string
  515. .IR s .
  516. If no newline is found after reading the first
  517. .IR n \-2
  518. characters,
  519. .I fgetsmod
  520. will add a newline and null character, then
  521. read and discard characters from the
  522. input stream up to a newline.  In other words, it
  523. will discard characters in excess of the
  524. capacity of
  525. .IR s .
  526. The last character read into
  527. .I s
  528. is followed by a null character.
  529. .I Fgetsmod
  530. returns the number of characters read
  531. if it can read the whole line.
  532. .SH "SEE ALSO"
  533. gets(3S),
  534. puts(3S),
  535. getc(3S),
  536. scanf(3S),
  537. fread(3S),
  538. ferror(3S)
  539. .SH AUTHOR
  540. Mohamed el Lozy, Harvard Health Sciences Computing Faility
  541. .SH DIAGNOSTICS
  542. .I Fgetsmod
  543. return the integer zero
  544. upon end of file or read error.
  545. It returns \-2 (to avoid confusion with
  546. .BR \s9EOF\s0 )
  547. if the line is too long.
  548. .SH BUGS
  549. .I Fgetsmod
  550. returns an
  551. .IR int ,
  552. unlike
  553. .IR fgets ,
  554. which returns a
  555. .IR char .
  556. It should therefore probably return
  557. .SM
  558. .B EOF
  559. on end of file, but returning zero makes it
  560. more similar to
  561. .IR fgets .
  562. SHAR_EOF
  563. if test 1470 -ne "`wc -c < 'fgetsmod.3s'`"
  564. then
  565.     echo shar: error transmitting "'fgetsmod.3s'" '(should have been 1470 characters)'
  566. fi
  567. chmod +x 'fgetsmod.3s'
  568. fi
  569. echo shar: extracting "'fgetsmod.c'" '(1683 characters)'
  570. if test -f 'fgetsmod.c'
  571. then
  572.     echo shar: will not over-write existing file "'fgetsmod.c'"
  573. else
  574. cat << \SHAR_EOF > 'fgetsmod.c'
  575. #ifndef lint
  576. static char rcsid[] = "$Header: fgetsmod.c,v 1.3 86/05/05 14:19:17 root Exp $";
  577. #endif
  578.  
  579. #include    <stdio.h>
  580.  
  581. #define TOOLONG -2
  582.  
  583. /* New and improved version of fgets.  Unlike original, eats up extra chars.
  584.  * fgets1 will read n - 1 characters, or up to a new line, whichever
  585.  * comes first, from stream iop into string s.  The last character read
  586.  * into s is followed by a null character.
  587.  *
  588.  * It deals with all possibilities.  If line ends with newline or have
  589.  * isolated EOF, no problem.  Otherwise, it will insert a newline and eat
  590.  * any excess characters.  Hence guarantees line ending with newline
  591.  * followed by null.
  592.  *
  593.  * It returns:
  594.  *   1.  NULL at end of file, for compatible with fgets.
  595.  *   2.  TOOLONG if line is too long.
  596.  *       This is usable as a warning.
  597.  *   3.  Length of line, excluding null (like strlen), otherwise.
  598.  *       This is useful in the usual case when line is read uneventfully.
  599.  */
  600.  
  601. fgetsmod(s, n, iop)
  602. char *s;
  603. register FILE *iop;
  604. {
  605.     register c;
  606.     register char *cs;
  607.  
  608.     cs = s;
  609.  
  610.     while (--n > 0 && (c = getc(iop)) != EOF) {
  611.     *cs++ = c;
  612.     if (c == '\n')
  613.         break;
  614.     }
  615.  
  616.     if (c == '\n') {    /* normal ending, commonest case */
  617.     *cs = '\0';
  618.     return (cs - s);
  619.     }
  620.  
  621.     if ((c == EOF) && (cs == s))  /* isolated EOF, second commonest case */
  622.     return (NULL);
  623.  
  624.     if (n == 0) {    /* line too long */
  625.     *cs = '\0';
  626.     *(--cs) = '\n';    /* put in missing newline */
  627.     while ((c = getc(iop)) != EOF && c != '\n')    /* eat up extra chars */
  628.         ;
  629.     return (TOOLONG);
  630.     }
  631.  
  632.     if (c == EOF) {    /* final line has no newline -- rare */
  633.     *cs++ = '\n';
  634.     *cs = '\0';
  635.     return (cs - s);    /* pretend all was OK */
  636.     }
  637.     /* NOTREACHED */
  638. }
  639. SHAR_EOF
  640. if test 1683 -ne "`wc -c < 'fgetsmod.c'`"
  641. then
  642.     echo shar: error transmitting "'fgetsmod.c'" '(should have been 1683 characters)'
  643. fi
  644. chmod +x 'fgetsmod.c'
  645. fi
  646. echo shar: extracting "'fmtr.1'" '(3397 characters)'
  647. if test -f 'fmtr.1'
  648. then
  649.     echo shar: will not over-write existing file "'fmtr.1'"
  650. else
  651. cat << \SHAR_EOF > 'fmtr.1'
  652. .TH FMTR 1H LOCAL "4th Berkeley Distribution"
  653. .SH NAME
  654. fmtr \- simple formatter for
  655. .I roff
  656. source files
  657. .SH SYNOPSIS
  658. .B fmtr
  659. [ \-w
  660. .I width
  661. ] [ \-z ] [ \-s
  662. .I .s1.s2.s3 ... .sn
  663. ] [ \-e
  664. .I .e1.e2.e3 ... .en
  665. ] [ name ...  ]
  666. .SH DESCRIPTION
  667. .I Fmtr
  668. is a simple text formatter which evens out the lines of files prepared
  669. for submission to the Unix formatters
  670. .IR nroff ,
  671. .IR troff ,
  672. or
  673. .IR ditroff .
  674. .PP
  675. It is for users who find editing neatly formatted files pleasanter than
  676. working with files with irregular lines.  It knows that lines starting
  677. with a period or with an apostrophe should be output as is, and that
  678. certain requests and macros start no-fill mode, while others return to
  679. fill mode.  So, unlike
  680. .IR fmt ,
  681. it will process the following example properly:
  682. .sp
  683. .in +5
  684. .nf
  685. Some short lines
  686. to be joined,
  687. then a table which should be respected:
  688. \&.TS
  689. n n.
  690. 1    2
  691. 3    4
  692. \&.TE
  693. .nf
  694. More short
  695. lines to be joined.
  696. .in
  697. .sp
  698. .fi
  699. The above will produce:
  700. .sp
  701. .nf
  702. .in +5
  703. Some short lines to be joined, then a table which should be respected:
  704. \&.TS
  705. n n.
  706. 1    2
  707. 3    4
  708. \&.TE
  709. More short lines to be joined.
  710. .fi
  711. .sp
  712. .in
  713. .I Fmt
  714. run on that example would produce:
  715. .sp
  716. .in +5
  717. .nf
  718. Some short lines to be joined, then a table which should be respected:
  719. \&.TS
  720. n n.  1       2 3       4
  721. \&.TE
  722. More short lines to be joined.
  723. .fi
  724. .in
  725. .sp
  726. destroying the table.
  727. .PP
  728. .I Fmtr
  729. reads the concatenation of input files (or standard input if none are
  730. given) and produces on standard output a version of its input with lines
  731. as close as possible to 72 characters.  This default line length may be
  732. modified with the
  733. .IR \-w
  734. (for width) flag.  If you specify the
  735. .I \-z
  736. flag it will peek under a zero width character at the start of a line
  737. seeking a command.
  738. .I Fmtr
  739. knows about the
  740. .IR ms (7)
  741. and
  742. .IR me (7)
  743. macro packages.
  744. .PP
  745. Since
  746. .I fmtr
  747. knows and respects most formatter constructs, it can be safely used on
  748. an entire document, as long as that document does not contain any
  749. strange sequences of commands.
  750. .PP
  751. There are two ways in which additional macros that start or end no-fill
  752. mode may be specified to
  753. .IR fmtr .
  754. The
  755. .I \-s
  756. and
  757. .I \-e
  758. flags can be used to introduce sequences of macros that start and end
  759. no-fill mode respectively.  Any number of macros, each of which must
  760. consist of a period followed by exactly two letters, can be concatenated
  761. into a string.  So if you are using two macros,
  762. .I .OS
  763. and
  764. .IR .FS ,
  765. both of which start no-fill mode, and a corresponding pair,
  766. .I .OE
  767. and
  768. .IR .FE ,
  769. which end no-fill mode, you would enter
  770. .sp
  771. .ti +5
  772. .I "fmtr -s .OS.FS -e .OE.FE file"
  773. .sp
  774. For users who regularly use macros of there own, the environmental
  775. variables
  776. .I FMTR_S
  777. and
  778. .I FMTR_E
  779. are taken as strings of macros which start and end no-fill mode.  They
  780. should be in the same format as the strings used as arguments to the
  781. .I \-s
  782. and
  783. .I \-e
  784. flags.
  785. .SH "SEE ALSO"
  786. nroff(1), environ(7), ms(7), me(7)
  787. .SH AUTHOR
  788. Mohamed el Lozy, Health Sciences Computing Facility, Harvard School of
  789. Public Health
  790. .SH BUGS
  791. The program does not attempt to duplicate
  792. .IR fmt ,
  793. and will not deal with mail headers or with indented (preformatted)
  794. text.
  795. .br
  796. The current version does not support the
  797. .I mm
  798. macros, I hope to correct that soon.
  799. .br
  800. Macros recognized must be exactly two characters long.
  801. .br
  802. .I Fmtr
  803. is not a full
  804. .I roff
  805. recognizer.  For example, changing the definition of the control or the
  806. escape character, will cause confusion.
  807. SHAR_EOF
  808. if test 3397 -ne "`wc -c < 'fmtr.1'`"
  809. then
  810.     echo shar: error transmitting "'fmtr.1'" '(should have been 3397 characters)'
  811. fi
  812. chmod +x 'fmtr.1'
  813. fi
  814. echo shar: extracting "'fmtr.c'" '(3468 characters)'
  815. if test -f 'fmtr.c'
  816. then
  817.     echo shar: will not over-write existing file "'fmtr.c'"
  818. else
  819. cat << \SHAR_EOF > 'fmtr.c'
  820. #ifndef lint
  821. static char rcsid[] = "$Header: fmtr.c,v 1.4 86/05/05 14:09:41 root Exp $";
  822. #endif
  823.  
  824. /*  fmtr, a formatter for roff source text respecting no fill mode.
  825.  *  The code is derived from a translation to C of Kernighan and
  826.  *  Plaugher's format program.
  827.  */
  828.  
  829. #include "fmtr.h"
  830.  
  831. int optind, opterr;
  832. char *optarg, *progname;        /* program name */
  833.  
  834. main(argc, argv)
  835. int argc;
  836. char *argv[];
  837. {
  838.     int i, c;
  839.     char *sarray, *earray, *getenv();
  840.     FILE *efopen(), *fp;
  841.  
  842.     progname = argv[0];
  843.     outp = outbuf;
  844.     sarray = earray = (char *) NULL;
  845.     llength = 72;    /* default */
  846.  
  847.     sarray = getenv("FMTR_S");
  848.     check(sarray, "environmental variable FMTR_S");
  849.     earray = getenv("FMTR_E");
  850.     check(earray, "environmental variable FMTR_E");
  851.  
  852.     mk_table(sarray, earray);    /* make table of commands from env var */
  853.                 /* we call mk_table whether the arrays */
  854.                 /* null or not */
  855.     sarray = earray = (char *) 0;
  856.  
  857.     while ((c = getopt(argc, argv, "zw:s:e:")) != EOF)
  858.     switch (c) {
  859.         case 'z':
  860.         z_flag = 1;
  861.         break;
  862.         case 'w':
  863.         llength = atoi(optarg);
  864.         if (llength <= 0 || llength >= BUFSIZ - 2) {
  865.             fprintf(stderr,"%s: bad line length: %s\n", progname, optarg);
  866.             exit (1);
  867.         }
  868.         break;
  869.         case 's':
  870.         sarray = optarg;
  871.         check(sarray, "-s flag");
  872.         break;
  873.         case 'e':
  874.         earray = optarg;
  875.         check(earray, "-e flag");
  876.         break;
  877.         case '?':
  878.         usage();
  879.         exit(1);
  880.     }
  881.  
  882.     mk_table(sarray, earray);    /* make table of commands from flags */
  883.                 /* again, always call */
  884.  
  885.     argc -= optind;
  886.     argv += optind;
  887.     
  888.     fp = stdin;
  889.     i = 0;
  890.     do {
  891.     if (argc > 0)
  892.         fp = efopen(*argv, "r");
  893.     do_fmt(fp);        /* does real work */
  894.     argv++;
  895.     } while (++i < argc);
  896.  
  897.     n_brk();
  898. }
  899.  
  900. check(string, origin)    /* check format of string of macros */
  901. char *string, *origin;
  902. {
  903.     if (string == NULL)
  904.     return;
  905.     if ((string[0] != '.') || strlen(string) % 3 != 0) {
  906.     fprintf(stderr,
  907.     "%s: list of macros '%s' supplied with %s is not in correct format\n",
  908.     progname, string, origin);
  909.     exit(1);
  910.     }
  911. }
  912.  
  913. /*  do_fmt is where work is done, or rather assigned.  Each line
  914.  *  read has the terminatig newline, and any trailing blanks, removed.
  915.  *  Then it is sent to either command() or text(), depending on whether
  916.  *  it appears to be a command or not.
  917.  */
  918.  
  919. do_fmt(fp)
  920. FILE *fp;
  921. {
  922.     char *cp, line[10*BUFSIZ];
  923.     int length;
  924.  
  925.     while (length = fgetsmod(line, sizeof line, fp)) {
  926.     for (cp = &line[length - 2]; *cp == ' ' && cp >= line; cp--)
  927.         ;
  928.     *++cp = '\0';
  929.         if (*line == '.' || *line == '\'' ||
  930.       (z_flag && strncmp(line, "\\&.", 3) == 0) ||
  931.       (z_flag && strncmp(line, "\\&'", 3) == 0))
  932.             command(line);
  933.         else
  934.             text(line);
  935.     }
  936. }
  937.  
  938. /*  text() checks for leading blanks or blank line, in which case causes
  939.  *  break.  Blank lines output as is, others broken into words by getword()
  940.  *  with words sent to outbuf by putword().
  941.  */
  942.  
  943. text(line)
  944. char *line;
  945. {
  946.     char *pline, wordbuf[BUFSIZ], *getword();
  947.  
  948.     if (ul_val || ce_val || nf_val) {
  949.     puts(line);
  950.     if (ul_val)
  951.         ul_val--;
  952.     if (ce_val)
  953.         ce_val--;
  954.     return;
  955.     }
  956.  
  957.     if (*line == ' ' || *line == '\0')
  958.     leadbl(line);
  959.     if (*line == '\0')
  960.     put(line);
  961.     else {        /* main case */
  962.     pline = line;
  963.     do {
  964.         pline = getword(pline, wordbuf);
  965.         if (pline)
  966.             putword(wordbuf);
  967.     } while (pline);
  968.     }
  969. }
  970.  
  971. usage()
  972. {
  973.     fprintf(stderr,
  974. "usage: %s [ -z ] [ -wn ] [ -s start_string ] [ -e end_string] [ file ... ]\n",
  975.     progname);
  976. }
  977. SHAR_EOF
  978. if test 3468 -ne "`wc -c < 'fmtr.c'`"
  979. then
  980.     echo shar: error transmitting "'fmtr.c'" '(should have been 3468 characters)'
  981. fi
  982. chmod +x 'fmtr.c'
  983. fi
  984. echo shar: extracting "'fmtr.h'" '(365 characters)'
  985. if test -f 'fmtr.h'
  986. then
  987.     echo shar: will not over-write existing file "'fmtr.h'"
  988. else
  989. cat << \SHAR_EOF > 'fmtr.h'
  990. #include <stdio.h>
  991. #include <ctype.h>
  992.  
  993. int z_flag;        /* look behind zero width spaces? */
  994. int nf_val;        /* no fill on or off? */
  995. int ul_val;        /* number of lines to underline */
  996. int ce_val;        /* number of lines to center */
  997. int llength;
  998.  
  999. char outbuf[BUFSIZ];    /* output buffer, ridiculously large */
  1000. char *outp;        /* pointer into outbuf */
  1001.  
  1002. void n_brk();
  1003. char *strcpy();
  1004. SHAR_EOF
  1005. if test 365 -ne "`wc -c < 'fmtr.h'`"
  1006. then
  1007.     echo shar: error transmitting "'fmtr.h'" '(should have been 365 characters)'
  1008. fi
  1009. chmod +x 'fmtr.h'
  1010. fi
  1011. echo shar: extracting "'getopt.3'" '(2749 characters)'
  1012. if test -f 'getopt.3'
  1013. then
  1014.     echo shar: will not over-write existing file "'getopt.3'"
  1015. else
  1016. cat << \SHAR_EOF > 'getopt.3'
  1017. .TH GETOPT 3
  1018. .DA 25 March 1982
  1019. .SH NAME
  1020. getopt \- get option letter from argv
  1021. .SH SYNOPSIS
  1022. .ft B
  1023. int getopt(argc, argv, optstring)
  1024. .br
  1025. int argc;
  1026. .br
  1027. char **argv;
  1028. .br
  1029. char *optstring;
  1030. .sp
  1031. extern char *optarg;
  1032. .br
  1033. extern int optind;
  1034. .ft
  1035. .SH DESCRIPTION
  1036. .I Getopt
  1037. returns the next option letter in
  1038. .I argv
  1039. that matches a letter in
  1040. .IR optstring .
  1041. .I Optstring
  1042. is a string of recognized option letters;
  1043. if a letter is followed by a colon, the option is expected to have
  1044. an argument that may or may not be separated from it by white space.
  1045. .I Optarg
  1046. is set to point to the start of the option argument on return from
  1047. .IR getopt .
  1048. .PP
  1049. .I Getopt
  1050. places in
  1051. .I optind
  1052. the
  1053. .I argv
  1054. index of the next argument to be processed.
  1055. Because
  1056. .I optind
  1057. is external, it is normally initialized to zero automatically
  1058. before the first call to 
  1059. .IR getopt .
  1060. .PP
  1061. When all options have been processed (i.e., up to the first
  1062. non-option argument),
  1063. .I getopt
  1064. returns
  1065. .BR EOF .
  1066. The special option
  1067. .B \-\-
  1068. may be used to delimit the end of the options;
  1069. .B EOF
  1070. will be returned, and
  1071. .B \-\-
  1072. will be skipped.
  1073. .SH SEE ALSO
  1074. getopt(1)
  1075. .SH DIAGNOSTICS
  1076. .I Getopt
  1077. prints an error message on
  1078. .I stderr
  1079. and returns a question mark
  1080. .RB ( ? )
  1081. when it encounters an option letter not included in
  1082. .IR optstring .
  1083. .SH EXAMPLE
  1084. The following code fragment shows how one might process the arguments
  1085. for a command that can take the mutually exclusive options
  1086. .B a
  1087. and
  1088. .BR b ,
  1089. and the options
  1090. .B f
  1091. and
  1092. .BR o ,
  1093. both of which require arguments:
  1094. .PP
  1095. .RS
  1096. .nf
  1097. main(argc, argv)
  1098. int argc;
  1099. char **argv;
  1100. {
  1101.     int c;
  1102.     extern int optind;
  1103.     extern char *optarg;
  1104.     \&.
  1105.     \&.
  1106.     \&.
  1107.     while ((c = getopt(argc, argv, "abf:o:")) != EOF)
  1108.         switch (c) {
  1109.         case 'a':
  1110.             if (bflg)
  1111.                 errflg++;
  1112.             else
  1113.                 aflg++;
  1114.             break;
  1115.         case 'b':
  1116.             if (aflg)
  1117.                 errflg++;
  1118.             else
  1119.                 bproc();
  1120.             break;
  1121.         case 'f':
  1122.             ifile = optarg;
  1123.             break;
  1124.         case 'o':
  1125.             ofile = optarg;
  1126.             break;
  1127.         case '?':
  1128.         default:
  1129.             errflg++;
  1130.             break;
  1131.         }
  1132.     if (errflg) {
  1133.         fprintf(stderr, "Usage: ...");
  1134.         exit(2);
  1135.     }
  1136.     for (; optind < argc; optind++) {
  1137.         \&.
  1138.         \&.
  1139.         \&.
  1140.     }
  1141.     \&.
  1142.     \&.
  1143.     \&.
  1144. }
  1145. .RE
  1146. .PP
  1147. A template similar to this can be found in
  1148. .IR /usr/pub/template.c .
  1149. .SH HISTORY
  1150. Written by Henry Spencer, working from a Bell Labs manual page.
  1151. Behavior believed identical to the Bell version.
  1152. .SH BUGS
  1153. It is not obvious how
  1154. `\-'
  1155. standing alone should be treated;  this version treats it as
  1156. a non-option argument, which is not always right.
  1157. .PP
  1158. Option arguments are allowed to begin with `\-';
  1159. this is reasonable but reduces the amount of error checking possible.
  1160. .PP
  1161. .I Getopt
  1162. is quite flexible but the obvious price must be paid:  there is much
  1163. it could do that it doesn't, like
  1164. checking mutually exclusive options, checking type of
  1165. option arguments, etc.
  1166. SHAR_EOF
  1167. if test 2749 -ne "`wc -c < 'getopt.3'`"
  1168. then
  1169.     echo shar: error transmitting "'getopt.3'" '(should have been 2749 characters)'
  1170. fi
  1171. chmod +x 'getopt.3'
  1172. fi
  1173. echo shar: extracting "'getopt.c'" '(1608 characters)'
  1174. if test -f 'getopt.c'
  1175. then
  1176.     echo shar: will not over-write existing file "'getopt.c'"
  1177. else
  1178. cat << \SHAR_EOF > 'getopt.c'
  1179. #ifndef lint
  1180. static char rcsid[] = "$Header: getopt.c,v 1.3 86/05/05 14:20:35 root Exp $";
  1181. #endif
  1182.  
  1183. /* got this off net.sources */
  1184. #include <stdio.h>
  1185.  
  1186. /*
  1187.  * get option letter from argument vector
  1188.  */
  1189. int    opterr = 1,    /* if set to zero no message for bad option */
  1190.     optind = 1,    /* index into parent argv vector */
  1191.     optopt;        /* character checked for validity */
  1192. char    *optarg;    /* argument associated with option */
  1193.  
  1194. #define BADCH    (int)'?'
  1195. #define EMSG    ""
  1196.  
  1197. getopt(nargc,nargv,ostr)
  1198. int    nargc;
  1199. char    **nargv,
  1200.     *ostr;
  1201. {
  1202.     static char    *place = EMSG;    /* option letter processing */
  1203.     register char    *oli;        /* option letter list index */
  1204.     char    *index();
  1205.  
  1206.     if(!*place) {            /* update scanning pointer */
  1207.     if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place)
  1208.         return(EOF);
  1209.     if (*place == '-') {    /* found "--" */
  1210.         ++optind;
  1211.         return(EOF);
  1212.     }
  1213.     }                /* option letter okay? */
  1214.     if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) {
  1215.     if(!*place) ++optind;
  1216.     if (opterr)
  1217.         fprintf(stderr, "%s: illegal option -- %c\n", *nargv, optopt);
  1218.     return(BADCH);
  1219.     }
  1220.     if (*++oli != ':') {        /* don't need argument */
  1221.     optarg = NULL;
  1222.     if (!*place) ++optind;
  1223.     }
  1224.     else {                /* need an argument */
  1225.     if (*place) optarg = place;    /* no white space */
  1226.     else if (nargc <= ++optind) {    /* no arg */
  1227.         place = EMSG;
  1228.         if (opterr)
  1229.         fprintf(stderr, "%s: option requires an argument -- %c\n",
  1230.             *nargv, optopt);
  1231.         return(BADCH);
  1232.     }
  1233.      else optarg = nargv[optind];    /* white space */
  1234.     place = EMSG;
  1235.     ++optind;
  1236.     }
  1237.     return(optopt);            /* dump back option letter */
  1238. }
  1239. SHAR_EOF
  1240. if test 1608 -ne "`wc -c < 'getopt.c'`"
  1241. then
  1242.     echo shar: error transmitting "'getopt.c'" '(should have been 1608 characters)'
  1243. fi
  1244. chmod +x 'getopt.c'
  1245. fi
  1246. echo shar: extracting "'lowtext.c'" '(2420 characters)'
  1247. if test -f 'lowtext.c'
  1248. then
  1249.     echo shar: will not over-write existing file "'lowtext.c'"
  1250. else
  1251. cat << \SHAR_EOF > 'lowtext.c'
  1252. #ifndef lint
  1253. static char rcsid[] = "$Header: lowtext.c,v 1.3 86/04/25 17:15:14 root Exp $";
  1254. #endif
  1255.  
  1256. #include "fmtr.h"
  1257.  
  1258. int ti_val;
  1259.  
  1260. /*  leadbl() deals with leading blanks, causes break, then sets
  1261.  *  ti_val to number of blanks, unless line is blank.  Then pulls
  1262.  *  line forewards, so text() now has line starting with text, with
  1263.  *  ti_val containing the needed indent.
  1264.  */
  1265.  
  1266. leadbl(line)
  1267. char *line;
  1268. {
  1269.     char *ip;
  1270.  
  1271.     n_brk();
  1272.  
  1273.     ip = line;
  1274.     while (*ip == ' ')
  1275.     ip++;
  1276.     if (*ip != '\n')
  1277.     ti_val += ip - line;
  1278.     (void) strcpy(line, ip);
  1279. }
  1280.  
  1281. /* n_brk() causes a break */
  1282.  
  1283. void n_brk()
  1284. {
  1285.     if (outp > outbuf) {
  1286.         outp--;    /* back off from EOS */
  1287.     while (*outp == ' ' && outp >= outbuf)    /* remove trailing blanks */
  1288.         outp--;
  1289.     *++outp  = '\0';
  1290.     put(outbuf);
  1291.     }
  1292.     outp = outbuf;
  1293. }
  1294.  
  1295. /*
  1296.  *    getword gets the next word plus trailing space
  1297.  *      from the array pointed to by pline and
  1298.  *    returns it in that pointed at by word.  If there are
  1299.  *    no further words on that line, it returns NULL.
  1300.  *    It returns a pointer to the start of the next word.
  1301.  */
  1302.  
  1303. char *getword(pline, word)
  1304. char *pline, *word;
  1305. {
  1306.     if (*pline == '\0') {
  1307.     *word = '\0';
  1308.     return(NULL);
  1309.     }
  1310.  
  1311.     while (*pline != ' ' && *pline != '\0') {
  1312.     if (*pline == '\\' && isspace(pline[1])) /* get escaped space in word */
  1313.         *word++ = *pline++;
  1314.         *word++ = *pline++;
  1315.     }
  1316.  
  1317.     /*  get trailing spaces, and guarantee spaces at end of line;
  1318.      *  normally one but two at end of sentence.
  1319.      */
  1320.  
  1321.     if (*pline == '\0') {
  1322.     char *cptmp = pline;
  1323.  
  1324.     *word++ = ' ';
  1325.     while (any(*--cptmp, "\"']})"))
  1326.         ;
  1327.     if (any(*cptmp, ".:!?"))
  1328.         *word++ = ' ';
  1329.     }
  1330.     while (*pline == ' ')
  1331.     *word++ = *pline++;
  1332.     *word = '\0';
  1333.     return(pline);
  1334. }
  1335.  
  1336. putword(word)    /* put word into output buffer */
  1337. char *word;
  1338. {
  1339.     int s, t;    /* not needed, but greatly simplify if */
  1340.  
  1341.     t = strlen(word) - 1;    /* -1 for one trailing blank */
  1342.     s = outp - outbuf;
  1343.     if (s + t <= llength - ti_val) {
  1344.     for (; *word; *outp++ = *word++)
  1345.         ;
  1346.     return;
  1347.     }
  1348.     n_brk();
  1349.     for (; *word; *outp++ = *word++)
  1350.     ;
  1351. }
  1352.  
  1353. put(line)    /* output routine, separate as is more complex in original */
  1354. char *line;
  1355. {
  1356.     int i;
  1357.  
  1358.     for (i = 1; i <= ti_val; i++)
  1359.     putchar(' ');
  1360.     ti_val = 0;
  1361.     puts(line);
  1362. }
  1363.  
  1364. any(ch, string)    /* returns true if character is in string */
  1365. char ch;
  1366. char *string;
  1367. {
  1368.     while (*string)
  1369.     if (ch == *string++)
  1370.         return(1);
  1371.     return(0);
  1372. }
  1373. SHAR_EOF
  1374. if test 2420 -ne "`wc -c < 'lowtext.c'`"
  1375. then
  1376.     echo shar: error transmitting "'lowtext.c'" '(should have been 2420 characters)'
  1377. fi
  1378. chmod +x 'lowtext.c'
  1379. fi
  1380. exit 0
  1381. #    End of shell archive
  1382.